#include "State_Game_Play.h"

GamePlay* GamePlay::init = 0;
extern D3DApp* gd3dApp; 

extern IDirectInputDevice8		*mJoystick[MAXPLAYERS]; // This will be changed to an area later...
extern DIJOYSTATE2				js[MAXPLAYERS];

GamePlay::GamePlay()
{
	//set up camera info
	mCameraRadius    = 10.0f;
	mCameraRotationY = 0.5 * D3DX_PI;
	mCameraHeight    = 5.0f;

	for(int i = 0; i < MAXPLAYERS; i++)
	{
		ballx[i] = 0.0f;
		ballz[i] = 0.0f;
	}

	//set up players (balls)
	ball[0] = new Ball(0.5f, 20, 20, 3.0f, 0.0f, 3.0f, 10);
	ball[1] = new Ball(0.5f, 20, 20, 5.0f, 0.0f, 5.0f, 50);
	setupFX();
	
}

void GamePlay::Enter()
{
	
}

void GamePlay::Execute(D3DApp* g)
{
	//update();
	Draw();	
}

void GamePlay::Exit()
{
	//cout << "Exiting State_TwoPlayer\n";
}

void GamePlay::Draw()
{
	// The camera position/orientation relative to world space can 
	// change every frame based on input, so we need to rebuild the
	// view matrix every frame with the latest changes.
	buildViewMtx();	
	
	// Clear the backbuffer and depth buffer.
	HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0));

	HR(gd3dDevice->BeginScene());

	//setup rendering technique
	HR(mFX->SetTechnique(mhTech));

	//set effect parameters for initial use
	HR(mFX->SetMatrix(mhWVP, &(mView*mProj)));

	//begin pass
	UINT numPasses = 0;

	//begin fx rendering
	HR(mFX->Begin(&numPasses, 0));
	for(int j = 0; j < MAXPLAYERS; j++)
	{
		for(int i =0; i < numPasses; i++)
		{
			HR(mFX->BeginPass(i));
		//definition for translation maxtrix
		D3DXMATRIX T;
		D3DXMatrixTranslation(&T, ball[j]->getPosX(), 0.0f, ball[j]->getPosZ());
		HR(mFX->SetMatrix(mhWVP, &(T*mView*mProj)));
		HR(mFX->CommitChanges());
		 
		//jon this section will probably change, but it shdn't affect u
		ball[j]->draw();

		HR(mFX->EndPass());
		}
	}

	HR(mFX->End());
	HR(gd3dDevice->EndScene());


	//mGfxStats->display();

	

	// Present the backbuffer.
	HR(gd3dDevice->Present(0, 0, 0, 0));
}
GamePlay* GamePlay::Instance()
{
	if(init == 0)
	{
		init = new GamePlay();
	}
	return init;
}

void GamePlay::setupFX()
{
	mFX = 0;

	ID3DXBuffer* mFXErrors = 0;

	//create the effects object, if there are errors, display them
		//the following functions takes in the device to attach it too (our Direct3DDevice),
		//the name of the file to pull the effects from
		//defines & includes -- this shd be 0 for us
		//any flags (I want to know if we have errors so I set the Debug flag)
		//the next parameter specifies if we are sharing variables in the fx file with other fx files, we
		//aren't so it is 0
		//the last two parameters are the addresses of pointers to the fx object and the error buffer
	HR(D3DXCreateEffectFromFile(gd3dDevice, "BallSmackers.fx", 0, 0, D3DXSHADER_DEBUG, 0, &mFX, &mFXErrors));

	if( mFXErrors )
		MessageBox(0, (char*)mFXErrors->GetBufferPointer(), 0, 0);

	//get handles
	mhTech = mFX->GetTechniqueByName("TransformTech");
	mhWVP = mFX->GetParameterByName(0, "gWVP");
}

//the following functions help set up the camera and the world view
void GamePlay::buildViewMtx()
{
	float x = mCameraRadius * cosf(mCameraRotationY);
	float z = mCameraRadius * sinf(mCameraRotationY);
	D3DXVECTOR3 pos(x, mCameraHeight, z);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMatrixLookAtLH(&mView, &pos, &target, &up);
}

void GamePlay::buildProjMtx()
{
	float w = WIDTH;
	float h = HEIGHT;
	D3DXMatrixPerspectiveFovLH(&mProj, D3DX_PI * 0.25f, w/h, 1.0f, 5000.0f);
}
void GamePlay::cleanUp()
{
	ReleaseCOM(mFX);
}

void GamePlay::update(float dt)
{
	//i think we will neeed arrays of some of these variables if we do not move this to the ball class
	static float cursorDelay = 0.0f;
	static float timeAccum = 0.0f;
	static int prevJS = 0;
	for(int i = 0; i < MAXPLAYERS; i++)
	{
		mJoystick[i]->GetDeviceState(sizeof(DIJOYSTATE2), &js[i]);
		/*	
		if(js[i].rgdwPOV[0] == 0)
		{
			//ballz[i] = -(dt * 5);
			ballz[i] = (dt * changeVelZ(ball[i], -50, dt));
			//ball[i]->updatePosZ(ballz[i]);
		}

		else if(js[i].rgdwPOV[0] == 9000)
		{
			//ballx[i] = -(dt * 5);
			ballx[i] = (dt * changeVelX(ball[i], -50, dt));
			//ball[i]->updatePosX(ballx[i]);
		}

		else if(js[i].rgdwPOV[0] == 18000)
		{
			//ballz[i] = dt * 5;
			ballz[i] = (dt * changeVelZ(ball[i], 50, dt));
			//ball[i]->updatePosZ(ballz[i]);
		}

		else if(js[i].rgdwPOV[0] == 27000)
		{
			//ballx[i] = dt * 5;
			ballx[i] = (dt * changeVelX(ball[i], 50, dt));
			//ball[i]->updatePosX(ballx[i]);
		}
		*/

		//else if((js[i].rgbButtons[0] & 0x80) && (prevJSButtons[0] ^ js[i].rgbButtons[0]))
		if((js[i].rgbButtons[0] & 0x80) && (prevJSButtons[0] ^ js[i].rgbButtons[0]))
		{
			
		}

		else if(js[i].rgbButtons[1] & 0x80 && (prevJSButtons[1] ^ js[i].rgbButtons[1]))
		{
			
		}

		else if(js[i].rgbButtons[2] & 0x80 && (prevJSButtons[2] ^ js[i].rgbButtons[2]))
		{
			
		}

		else if(js[i].rgbButtons[3] & 0x80 && (prevJSButtons[3] ^ js[i].rgbButtons[3]))
		{
			
		}
		else if(js[i].rgbButtons[8] & 0x80 && (prevJSButtons[8] ^ js[i].rgbButtons[8]))
		{
			PostQuitMessage(0);
		}

		ballx[i] = (dt * changeVelX(ball[i], -js[i].lX, dt));
		ballz[i] = (dt * changeVelZ(ball[i], js[i].lY, dt));


		if(ball[i]->getVelX() < 0)
			ball[i]->updateVelX(DRAG * pow(ball[i]->getVelX(), 2) * dt / ball[i]->getMass());
		else
            ball[i]->updateVelX(-DRAG * pow(ball[i]->getVelX(), 2) * dt / ball[i]->getMass());

		if(ball[i]->getVelZ() < 0)
			ball[i]->updateVelZ(DRAG * pow(ball[i]->getVelZ(), 2) * dt / ball[i]->getMass());
		else
            ball[i]->updateVelZ(-DRAG * pow(ball[i]->getVelZ(), 2) * dt / ball[i]->getMass());

		collision();

		ballx[i] = (ball[i]->getVelX() * dt);
		ballz[i] = (ball[i]->getVelZ() * dt);		

		ball[i]->updatePosX(ballx[i]);
		ball[i]->updatePosZ(ballz[i]);
	}

	if(!(js[i].rgdwPOV[0] == 4500 || js[i].rgdwPOV[0] == 13500 || js[i].rgdwPOV[0] == 22500 || js[i].rgdwPOV[0] == 31500))
			prevJS = js[i].rgdwPOV[0];

	for(int i = 0; i < 12; i++)
	{
		prevJSButtons[i] = js[i].rgbButtons[i];
	}
	
}

void GamePlay::collision()
{
	for(int i = 0; i < MAXPLAYERS; i++)
	{
		for(int j = 0; j < MAXPLAYERS; j++)
		{
			if(ball[i] == ball[j])
				continue;
			if(distance(ball[i], ball[j]) < (ball[i]->getRadius() + ball[j]->getRadius()))
			{
				float v1[2] = {0}, v2[2] = {0};
//				v2[0] = ((ball[i]->getMass() * ball[i]->getVelX()) + (ball[j]->getMass() * ball[j]->getVelX()) + (ball[i]->getMass() * (ball[i]->getVelX() - ball[j]->getVelX()))) / (ball[i]->getMass() + ball[j]->getMass());
//				v2[1] = ((ball[i]->getMass() * ball[i]->getVelZ()) + (ball[j]->getMass() * ball[j]->getVelZ()) + (ball[i]->getMass() * (ball[i]->getVelZ() - ball[j]->getVelZ()))) / (ball[i]->getMass() + ball[j]->getMass());

//				v1[0] = -1 * (ball[i]->getVelX() - ball[j]->getVelX()) + v2[0];
//				v1[1] = -1 * (ball[i]->getVelZ() - ball[j]->getVelZ()) + v2[1];

				float normX = (ball[i]->getPosX() - ball[j]->getPosX()) / distance(ball[i], ball[j]);
				float normZ = (ball[i]->getPosZ() - ball[j]->getPosZ()) / distance(ball[i], ball[j]);

				float norm = sqrt(pow(normX, 2) + pow(normZ, 2));

				float a1 = (ball[i]->getVelX() * normX) + (ball[i]->getVelZ() * normZ);
				float a2 = (ball[j]->getVelX() * normX) + (ball[j]->getVelZ() * normZ);

				float p = 2 * (a1 - a2) / (ball[i]->getMass() + ball[j]->getMass());

				v1[0] = ball[i]->getVelX() - p * ball[j]->getMass() * normX;
				v1[1] = ball[i]->getVelZ() - p * ball[j]->getMass() * normZ;

				v2[0] = ball[j]->getVelX() - p * ball[i]->getMass() * normX;
				v2[1] = ball[j]->getVelZ() - p * ball[i]->getMass() * normZ;

				ball[i]->setVelX(v1[0]);
				ball[i]->setVelZ(v1[1]);

				ball[j]->setVelX(v2[0]);
				ball[j]->setVelZ(v2[1]);
				return;
				//PostQuitMessage(0);
			}
		}
	}
}

double GamePlay::distance(Ball* b1, Ball* b2)
{
	return sqrt(pow(b1->getPosX() - b2->getPosX(),2) + pow(b1->getPosY() - b2->getPosY(),2) + pow(b1->getPosZ() - b2->getPosZ(),2));
}

double GamePlay::changeVelX(Ball* & b, int force, float dt)
{
	float vel;

	if(force > MAXFORCE)
		force = MAXFORCE;
	else if (force < -MAXFORCE)
		force = -MAXFORCE;
    
	vel = force * dt / b->getMass();
	if(vel > MAXVELOCITY)
		vel = MAXVELOCITY;
	else if(vel < -MAXVELOCITY)
		vel = -MAXVELOCITY;

	b->updateVelX(vel);
	return b->getVelX();
}

double GamePlay::changeVelZ(Ball* & b, int force, float dt)
{
	float vel;

	if(force > MAXFORCE)
		force = MAXFORCE;
	else if (force < -MAXFORCE)
		force = -MAXFORCE;
    
	vel = force * dt / b->getMass();
	if(vel > MAXVELOCITY)
		vel = MAXVELOCITY;
	else if(vel < -MAXVELOCITY)
		vel = -MAXVELOCITY;

	b->updateVelZ(vel);
	return b->getVelZ();
}